/*
 * JukeboxProperties.java
 *
 * Created on March 3, 2005, 8:40 AM
 */
package com.bff.startup;

import com.bff.bjj.utils.ManifestReader;
import java.awt.Dimension;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.UIManager;

/**
 *
 * @author bill
 */
public class StartUpProperties {

    private static final String PROP_FILE = "startup.properties";
    private static final String PROP_FILE_STATE = "state.properties";
    private static final String PROP_FILE_DIR = System.getProperty("user.home")
            + System.getProperty("file.separator")
            + ".bjj";
    private static final String PROP_FILE_NAME = PROP_FILE_DIR
            + System.getProperty("file.separator")
            + PROP_FILE;
    private static final String PROP_FILE_NAME_STATE = PROP_FILE_DIR
            + System.getProperty("file.separator")
            + PROP_FILE_STATE;
    private static final String PROP_STARTUP_TYPE = "type";
    private static final String PROP_LAF = "laf";
    private String laf;
    private boolean useLastState;
    /**
     * State props
     */
    private static final String PROP_STATE = "state";
    private static final String PROP_STATE_X = "x";
    private static final String PROP_STATE_Y = "y";
    private static final String PROP_STATE_WIDTH = "width";
    private static final String PROP_STATE_HEIGHT = "height";
    private static final String PROP_VERSION = "Implementation-Version";
    private static final String SNAPSHOT = "SNAPSHOT";
    private static final String DEVELOPER = "Development";
    private static final String PROP_USE_LAST_STATE = "last";
    private static StartUpProperties instance;
    private StartupType type;
    private static String version;
    public static final String LAF_DEFAULT = "Nimbus";
    public static final String LAF_SYSTEM = "System";
    public static final String PROP_STATE_MINI = "mini";
    private StartupState startupState;
    private static BlockingQueue<StartupState> stateQueue = new LinkedBlockingQueue<StartupState>();
    private static Thread writeThread;

    /**
     * @return the startupState
     */
    public StartupState getStartupState() {
        return startupState;
    }

    /**
     * @param startupState the startupState to set
     */
    public void setStartupState(StartupState startupState) {
        this.startupState = startupState;
    }

    public enum StartupType {

        BOTH(0),
        MPD(1),
        SQUEEZE(2);

        StartupType(int id) {
            this.id = id;
        }
        private int id;

        /**
         * @return the description
         */
        public int getId() {
            return id;
        }
    }

    /**
     * Returns singleton properties class.  If the properties file exists the
     * properties are loaded.  If not everything will be null so make sure to
     * check exists() if necessary.
     * 
     * @return
     */
    public static StartUpProperties getInstance() {
        if (instance == null) {
            writeThread = new Thread(new StateWriter(), "State writer");
            writeThread.start();
            instance = new StartUpProperties();
        }

        return instance;
    }
    private boolean fileExists;

    private StartUpProperties() {
        try {
            loadProperties();
        } catch (IOException ex) {
            Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
            this.type = StartupType.BOTH;
        }
    }

    private synchronized void loadProperties() throws IOException {
        FileInputStream inFile = null;
        try {
            inFile = new FileInputStream(PROP_FILE_NAME);
            Properties props = new Properties();
            props.load(inFile);

            fileExists = true;

            int i = Integer.parseInt(props.getProperty(PROP_STARTUP_TYPE,
                    Integer.toString(StartupType.BOTH.getId())));

            if (i == StartupType.SQUEEZE.getId()) {
                setType(StartupType.SQUEEZE);
            } else if (i == StartupType.MPD.getId()) {
                setType(StartupType.MPD);
            } else {
                setType(StartupType.BOTH);
            }

            setLaf(props.getProperty(PROP_LAF, LAF_DEFAULT));
            setUseLastState(Boolean.parseBoolean(props.getProperty(PROP_USE_LAST_STATE, "false")));
            if (isUseLastState()) {
                loadStateProperties();
            }
        } finally {
            try {
                inFile.close();
            } catch (Exception ex) {
                Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void endWriter() {
        writeThread.interrupt();
    }

    private void loadStateProperties() {
        FileInputStream inFile = null;
        try {
            inFile = new FileInputStream(PROP_FILE_NAME_STATE);
            Properties props = new Properties();
            props.load(inFile);

            StartupState ss = null;

            String str = props.getProperty(PROP_STATE, StartupState.State.STATE_MAX.getDescription());
            for (StartupState.State s : StartupState.State.values()) {
                if (s.getDescription().equalsIgnoreCase(str)) {
                    ss = new StartupState(s);
                    break;
                }
            }

            if (ss == null) {
                return;
            } else {
                ss.setMiniPlayer(Boolean.parseBoolean(props.getProperty(PROP_STATE_MINI, "false")));
                ss.setX(Integer.parseInt(props.getProperty(PROP_STATE_X, "0")));
                ss.setY(Integer.parseInt(props.getProperty(PROP_STATE_Y, "0")));
                int width = Integer.parseInt(props.getProperty(PROP_STATE_WIDTH, "1024"));
                int height = Integer.parseInt(props.getProperty(PROP_STATE_HEIGHT, "1024"));
                ss.setDimension(new Dimension(width, height));
                setStartupState(ss);
            }
        } catch (Exception e) {
            setStartupState(null);
        } finally {
            try {
                inFile.close();
            } catch (Exception ex) {
                Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public boolean saveSettings(StartupType type) {
        return saveSettings(type, getLaf(), isUseLastState());
    }

    public boolean saveSettings(StartupType type, String laf) {
        return saveSettings(type, laf, isUseLastState());
    }

    public boolean saveSettings(String laf) {
        return saveSettings(getType(), laf, isUseLastState());
    }

    public boolean saveSettings(StartupType type, String laf, boolean useLastState) {

        try {
            setLaf(laf);
            setUseLastState(useLastState);
            Properties props = new Properties();
            props.setProperty(PROP_STARTUP_TYPE, Integer.toString(type.getId()));
            props.setProperty(PROP_LAF, laf);
            props.setProperty(PROP_USE_LAST_STATE, Boolean.toString(useLastState));
            File dir = new File(PROP_FILE_DIR);
            if (!dir.exists()) {
                dir.mkdir();
            }

            FileOutputStream outFile = null;
            try {
                outFile = new FileOutputStream(PROP_FILE_NAME);
                props.store(outFile, null);
            } catch (Exception e) {
                throw e;
            } finally {
                outFile.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
            return (false);
        }
        return (true);
    }

    /**
     * @return the type
     */
    public StartupType getType() {
        return type;
    }

    /**
     * @param type the type to set
     */
    public void setType(StartupType type) {
        this.type = type;
    }

    public boolean hasProps() {
        return fileExists;
    }

    public boolean isDeveloper() {
        return getVersion().equalsIgnoreCase(DEVELOPER);
    }

    public static String getVersion() {
        if (version == null) {
            try {
                File jarFile = new File(StartUpProperties.class.getProtectionDomain().getCodeSource().getLocation().toURI());
                if (jarFile.isFile()) {
                    try {
                        Attributes attr = ManifestReader.readManifestMainAttributes(jarFile);
                        for (Object a : attr.keySet()) {
                            Name name = (Name) a;
                            if (name.toString().equalsIgnoreCase(PROP_VERSION)) {
                                version = attr.getValue(name).trim();
                            }
                        }
                    } catch (IOException ex) {
                        Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } else {
                    version = DEVELOPER;
                }
            } catch (URISyntaxException ex) {
                version = DEVELOPER;
                Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return version;
    }

    public String getLAFClassName() {
        for (int i = 0; i < UIManager.getInstalledLookAndFeels().length; i++) {
            UIManager.LookAndFeelInfo lookAndFeelInfo = UIManager.getInstalledLookAndFeels()[i];
            if (lookAndFeelInfo.getName().equals(getLaf())) {
                return lookAndFeelInfo.getClassName();
            }
        }

        return UIManager.getSystemLookAndFeelClassName();
    }

    public static Collection<String> getAvailableLAFs() {
        List<String> lafs = new ArrayList<String>();

        for (int i = 0; i < UIManager.getInstalledLookAndFeels().length; i++) {
            UIManager.LookAndFeelInfo lookAndFeelInfo = UIManager.getInstalledLookAndFeels()[i];
            lafs.add(lookAndFeelInfo.getName());
        }
        return lafs;
    }

    /**
     * @return the laf
     */
    public String getLaf() {
        return laf != null ? laf : LAF_DEFAULT;
    }

    /**
     * @param aLaf the laf to set
     */
    public void setLaf(String aLaf) {
        laf = aLaf;
    }

    public void saveSettings(boolean rememberState) {
        saveSettings(getType(), getLaf(), rememberState);
    }

    public void writeState(StartupState state) {
        if (isUseLastState()) {
            if (state != null) {
                stateQueue.add(state);
            }
        }
    }

    /**
     * @return the useLastState
     */
    public boolean isUseLastState() {
        return useLastState;
    }

    /**
     * @param useLastState the useLastState to set
     */
    public void setUseLastState(boolean useLastState) {
        this.useLastState = useLastState;
    }

    private static class StateWriter implements Runnable {

        StateWriter() {
        }

        @Override
        public void run() {
            try {
                while (true) {
                    write(stateQueue.take());
                }
            } catch (InterruptedException ex) {
            }
        }

        private void write(StartupState ss) {
            Properties props = new Properties();
            props.setProperty(PROP_STATE, ss.getState().getDescription());
            props.setProperty(PROP_STATE_MINI, Boolean.toString(ss.isMiniPlayer()));

            if (ss.getState() == StartupState.State.STATE_SIZED) {
                props.setProperty(PROP_STATE_WIDTH, Integer.toString((int) ss.getDimension().getWidth()));
                props.setProperty(PROP_STATE_HEIGHT, Integer.toString((int) ss.getDimension().getHeight()));
                props.setProperty(PROP_STATE_X, Integer.toString(ss.getX()));
                props.setProperty(PROP_STATE_Y, Integer.toString(ss.getY()));
            }

            File dir = new File(PROP_FILE_DIR);
            if (!dir.exists()) {
                dir.mkdir();
            }

            FileOutputStream outFile = null;
            try {
                outFile = new FileOutputStream(PROP_FILE_NAME_STATE);
                props.store(outFile, null);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    outFile.close();
                } catch (IOException ex) {
                    Logger.getLogger(StartUpProperties.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    public boolean isSnapshot() {
        return getVersion().contains(SNAPSHOT);
    }
}
